package com.example.warehouseplus.webflux.handler;

import com.example.warehouseplus.entity.Item;
import com.example.warehouseplus.entity.ResponseResult;
import com.example.warehouseplus.service.ItemService;
import com.example.warehouseplus.utils.Limiting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import static org.springframework.web.reactive.function.BodyInserters.fromPublisher;
import static org.springframework.web.reactive.function.server.ServerResponse.ok;
@Slf4j
@Component
public class ItemHandler {

    @Autowired
    private  ItemService itemService;

    @Limiting(limitNum = 0.5, name = "limiting1")
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public Mono<ServerResponse> getAllItems(ServerRequest request) {
        log.info("Getting all items");
        return itemService.getAllItems()
                .collectList()
                .doOnNext(items -> log.info("Retrieved {} items", items.size()))
                .flatMap(items -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "OK", items)))
                .switchIfEmpty(ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(404, "No items found", null)));
    }


    @Limiting(limitNum = 0.5, name = "limiting2")
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public Mono<ServerResponse> getItemByName(ServerRequest request) {
        String name = request.pathVariable("name");
        log.info("Getting item by name: {}", name);
        return itemService.getItemByName(name)
                .doOnNext(item -> log.info("Retrieved item: {}", item))
                .flatMap(item -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "OK", item)))
                .switchIfEmpty(ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(404, "Not found", null)));
    }
    @Limiting(limitNum = 0.5, name = "limiting3")
    @PreAuthorize("hasRole('ADMIN')")
    public Mono<ServerResponse> createItem(ServerRequest request) {
        log.info("Creating item");
        return request.bodyToMono(Item.class)
                .flatMap(itemService::createItem)
                .doOnNext(item -> log.info("Created item: {}", item))
                .flatMap(item -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "Item created successfully", item)));
    }
    @Limiting(limitNum = 0.5, name = "limiting4")
    @PreAuthorize("hasRole('ADMIN')")
    public Mono<ServerResponse> updateItem(ServerRequest request) {
        String name = request.pathVariable("name");
        log.info("Updating item: {}", name);
        return request.bodyToMono(Item.class)
                .flatMap(item -> itemService.updateItem(name, item))
                .doOnNext(updatedItem -> log.info("Updated item: {}", updatedItem))
                .flatMap(updatedItem -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "Item updated successfully", updatedItem)));
    }
    @Limiting(limitNum = 0.5, name = "limiting5")
    @PreAuthorize("hasRole('ADMIN')")
    public Mono<ServerResponse> deleteItem(ServerRequest request) {
        String name = request.pathVariable("name");
        log.info("Deleting item: {}", name);
        return itemService.deleteItem(name)
                .then(ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "Item deleted successfully", null)));
    }
    @Limiting(limitNum = 0.5, name = "limiting6")
    @PreAuthorize("hasRole('ADMIN')")
    public Mono<ServerResponse> uploadItems(ServerRequest request) {
        log.info("Uploading items");
        Flux<Item> items = request.bodyToFlux(Item.class);
        return items.collectList()
                .doOnNext(list -> log.info("Uploading {} items", list.size()))
                .flatMap(list -> itemService.uploadItems(list).collectList())
                .doOnNext(itemsList -> log.info("Uploaded {} items successfully", itemsList.size()))
                .flatMap(itemsList -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "Items uploaded successfully", itemsList)));
    }
    @Limiting(limitNum = 0.5, name = "limiting7")
    @PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
    public Mono<ServerResponse> getItemsByPriceRange(ServerRequest request) {
        double minPrice = Double.parseDouble(request.queryParam("minPrice").orElse("0"));
        double maxPrice = Double.parseDouble(request.queryParam("maxPrice").orElse("0"));
        log.info("Getting items by price range: {} - {}", minPrice, maxPrice);
        return itemService.getItemsByPriceRange(minPrice, maxPrice)
                .collectList()
                .doOnNext(items -> log.info("Retrieved {} items in the price range", items.size()))
                .flatMap(items -> ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(200, "OK", items)))
                .switchIfEmpty(ServerResponse.ok()
                        .contentType(MediaType.APPLICATION_JSON)
                        .bodyValue(new ResponseResult<>(404, "No items found in the given price range", null)));
    }
}
